package com.steakliu.sun.registry;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.steakliu.sun.cache.registry.RegistryCenterServiceCache;
import com.steakliu.sun.common.helper.SunServiceHelper;
import com.steakliu.sun.common.meta.ServiceMeta;
import com.steakliu.sun.loadbalancer.api.LoadBalancer;
import com.steakliu.sun.registry.api.RegistryConfig;
import com.steakliu.sun.registry.api.RegistryService;
import com.steakliu.sun.registry.api.RegistryServiceExtender;
import com.steakliu.sun.registry.transfer.ServiceMetaTransfer;
import com.steakliu.sun.spi.annotation.Join;
import com.steakliu.sun.spi.loader.ExtensionLoader;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 功能说明： nacos服务注册
 * <p>
 * Original @Author: steakliu-刘牌, 2023-01-07  22:01
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
@Join
public class NacosRegistryService extends RegistryServiceExtender implements RegistryService {

    private final Logger LOGGER = LoggerFactory.getLogger(NacosRegistryService.class);

    private NamingService namingService;
    private LoadBalancer loadBalancer;

    @Override
    public void init(RegistryConfig registryConfig) throws Exception {
//        Properties properties = new Properties();
//        properties.put(PropertyKeyConst.NAMESPACE, "srpc");
//        properties.put(PropertyKeyConst.SERVER_ADDR, registryConfig.getRegistryAddress());
        this.namingService = NamingFactory.createNamingService(registryConfig.getRegistryAddress());
    }

    @Override
    public void register(ServiceMeta serviceMeta) throws Exception {
        String serviceName = SunServiceHelper.getServiceKey(serviceMeta.getServiceName(), serviceMeta.getVersion(), serviceMeta.getGroup());
        namingService.registerInstance(serviceName, this.buildInstance(serviceMeta));
        //监控服务变化
        this.monitor(serviceName);
    }

    @Override
    public void unregister(ServiceMeta serviceMeta) throws Exception {
        String serviceName = SunServiceHelper.getServiceKey(serviceMeta.getServiceName(), serviceMeta.getVersion(), serviceMeta.getGroup());
        Instance instance = this.buildInstance(serviceMeta);
        this.namingService.deregisterInstance(serviceName, instance);
    }

    @Override
    public ServiceMeta discovery(String serviceName, String directConnections, String loadBalance) throws Exception {
        loadBalancer = ExtensionLoader.getExtension(LoadBalancer.class, loadBalance);
        //服务直连，灰度发布
        if (null != directConnections && !directConnections.isEmpty()) {
            //从缓存中获取
            List<ServiceMeta> serviceMetas = RegistryCenterServiceCache.get(serviceName);
            if (null != serviceMetas) {
                return this.getTargetService(directConnections, serviceMetas, loadBalancer);
            }
            //缓存中不存在，从注册中心获取
            List<Instance> instances = this.namingService.selectInstances(serviceName, true);
            if (instances.size() > 0) {
                serviceMetas = ServiceMetaTransfer.getNacosCacheServiceMetaList(instances);
                return this.getTargetService(directConnections, serviceMetas, loadBalancer);
            }
        }
        //从缓存中取
        List<ServiceMeta> metas = RegistryCenterServiceCache.get(serviceName);
        if (null != metas && metas.size() > 0) {
            return loadBalancer.select(RegistryCenterServiceCache.get(serviceName));
        }
        //获取健康的实例
        List<Instance> instances = this.namingService.selectInstances(serviceName, true);
        ServiceMeta meta = null;
        if (instances.size() > 0) {
            List<ServiceMeta> serviceMetas = ServiceMetaTransfer.getNacosCacheServiceMetaList(instances);
            meta = loadBalancer.select(serviceMetas);
            //保存进缓存
            RegistryCenterServiceCache.set(serviceName, serviceMetas);
        }
        return meta;
    }

    @Override
    public void monitor(String serviceName) throws Exception {
        namingService.subscribe(serviceName, event -> {
            if (event instanceof NamingEvent) {
                LOGGER.info("服务名为【{}】的服务发生变化: ", ((NamingEvent) event).getServiceName());
                List<Instance> instances = ((NamingEvent) event).getInstances();
                if (null != instances && instances.size() > 0) {
                    List<ServiceMeta> serviceMetas = ServiceMetaTransfer.getNacosCacheServiceMetaList(instances);
                    //删除本地缓存，然后从新更新缓存
                    RegistryCenterServiceCache.set(serviceName, serviceMetas);
                } else {
                    RegistryCenterServiceCache.remove(serviceName);
                }
                LOGGER.info("最新服务列表：{}", JSONObject.toJSONString(RegistryCenterServiceCache.get(serviceName)));
            }
        });
    }

    @Override
    public void destroy() throws Exception {
        this.namingService.shutDown();
    }

    // 构建instance
    private Instance buildInstance(ServiceMeta serviceMeta) {
        Instance instance = new Instance();
        instance.setIp(serviceMeta.getHost());
        instance.setPort(serviceMeta.getPort());
        instance.setHealthy(true);
        instance.setWeight(serviceMeta.getWeight());
        Map<String, String> instanceMeta = new HashMap<>();
        instanceMeta.put("serviceName", serviceMeta.getServiceName());
        instanceMeta.put("host", serviceMeta.getHost());
        instanceMeta.put("port", String.valueOf(serviceMeta.getPort()));
        instanceMeta.put("version", serviceMeta.getVersion());
        instanceMeta.put("group", serviceMeta.getGroup());
        instanceMeta.put("tag", serviceMeta.getTag());
        instanceMeta.put("weight", String.valueOf(serviceMeta.getWeight()));
        instanceMeta.put("meta", JSONObject.toJSONString(serviceMeta));
        instance.setMetadata(instanceMeta);
        return instance;
    }
}
